﻿using System;
using System.Collections.Generic;
using Autodesk.Revit.DB;
using YArchitech.Revit.RevitExtension;
using YJKRevitDimensionText.Utility;

namespace YJKRevitDimensionText.DimCreator
{
	internal class ConnectionDimension
	{
		public ConnectionDimension(Autodesk.Revit.DB.Document doc)
		{
			this.doc = doc;
		}

		public Dimension Connection(Dimension sourceDim, List<Dimension> targetDims)
		{
			Dimension dimension = null;
			Transaction transaction = null;
			try
			{
				if (targetDims.Count == 0)
				{
					return sourceDim;
				}
				transaction = new Transaction(this.doc, "Connection");
				transaction.Start();
				DimensionType dimensionType = this.doc.GetElement(sourceDim.GetTypeId()) as DimensionType;
				string dimensionShareParam = this.GetDimensionShareParam(sourceDim, targetDims);
				Line dimBaseUnboundLine = this.GetDimBaseUnboundLine(sourceDim);
				List<ConnectionDimension.DimensionReferenceInfo> list = new List<ConnectionDimension.DimensionReferenceInfo>();
				this.GetDimReferenceInfos(sourceDim, dimBaseUnboundLine, list);
				foreach (Dimension dim in targetDims)
				{
					this.GetDimReferenceInfos(dim, dimBaseUnboundLine, list);
				}
				list.Sort(new ConnectionDimension.DimensionReferenceInfoComparer());
				List<string> list2 = new List<string>();
				ReferenceArray referenceArray = new ReferenceArray();
				foreach (ConnectionDimension.DimensionReferenceInfo dimensionReferenceInfo in list)
				{
					string item = dimensionReferenceInfo.DimReference.ConvertToStableRepresentation(this.doc);
					if (!list2.Contains(item))
					{
						Reference reference = DimUtility.ConvertDimReference(this.doc, dimensionReferenceInfo.DimReference);
						referenceArray.Append(reference);
						list2.Add(item);
					}
				}
				dimension = this.doc.Create.NewDimension(this.doc.ActiveView, dimBaseUnboundLine, referenceArray, dimensionType);
				if (!string.IsNullOrEmpty(dimensionShareParam))
				{
					DimPublicFunc.SetDimFlag(dimension, dimensionShareParam);
				}
				RevitDimensionExtension.ResetDimTextPos(dimension);
				this.doc.Delete(sourceDim.Id);
				foreach (Dimension dimension2 in targetDims)
				{
					this.doc.Delete(dimension2.Id);
				}
				transaction.Commit();
			}
			finally
			{
				if (transaction != null && (int)transaction.GetStatus() != 3)
				{
					transaction.RollBack();
				}
			}
			return dimension;
		}

		private Line GetDimBaseUnboundLine(Dimension dim)
		{
			Line line;
			if (dim.NumberOfSegments == 0)
			{
				line = (dim.Curve.Clone() as Line);
			}
			else
			{
                XYZ origin = dim.Segments.get_Item(0).Origin;
                XYZ origin2 = dim.Segments.get_Item(1).Origin;
				line = Line.CreateBound(origin, origin2);
			}
			line.MakeUnbound();
			return line;
		}

		private double GetParamByPointOnUnboundCurve(Curve cv, XYZ ptTest)
		{
			return cv.Project(ptTest).Parameter;
		}

		private void GetDimReferenceInfos(Dimension dim, Line lineUnboundDimBase, List<ConnectionDimension.DimensionReferenceInfo> lstDimInfos)
		{
			Document document = dim.Document;
			int numberOfSegments = dim.NumberOfSegments;
			if (numberOfSegments == 0)
			{
				Line line = dim.Curve as Line;
				BoundingBoxXYZ boundingBoxXYZ = dim.get_BoundingBox(dim.Document.ActiveView);
				XYZ xyz = boundingBoxXYZ.Min + (boundingBoxXYZ.Max - boundingBoxXYZ.Min) / 2.0;
				XYZ xyzpoint = line.Project(xyz).XYZPoint;
				double num = dim.Value.Value * 0.5;
				XYZ xyz2 = line.Direction.Normalize() * num;
				XYZ xyz3 = xyzpoint - xyz2;
                Reference dimRef = dim.References.get_Item(0);
				lstDimInfos.Add(new ConnectionDimension.DimensionReferenceInfo(document, dimRef)
				{
					Point = xyz3,
					DistParam = this.GetParamByPointOnUnboundCurve(lineUnboundDimBase, xyz3)
				});
				XYZ xyz4 = xyzpoint + xyz2;
                Reference dimRef2 = dim.References.get_Item(1);
				lstDimInfos.Add(new ConnectionDimension.DimensionReferenceInfo(document, dimRef2)
				{
					Point = xyz4,
					DistParam = this.GetParamByPointOnUnboundCurve(lineUnboundDimBase, xyz4)
				});
				return;
			}
			Line dimBaseUnboundLine = this.GetDimBaseUnboundLine(dim);
			for (int i = 0; i < numberOfSegments; i++)
			{
                DimensionSegment dimensionSegment = dim.Segments.get_Item(i);
				if (dimensionSegment.Value != null)
				{
					XYZ origin = dimensionSegment.Origin;
					double num2 = dimensionSegment.Value.Value * 0.5;
					XYZ xyz5 = dimBaseUnboundLine.Direction.Normalize() * num2;
					XYZ xyz6 = origin - xyz5;
                    Reference dimRef3 = dim.References.get_Item(i);
					lstDimInfos.Add(new ConnectionDimension.DimensionReferenceInfo(document, dimRef3)
					{
						Point = xyz6,
						DistParam = this.GetParamByPointOnUnboundCurve(lineUnboundDimBase, xyz6)
					});
					XYZ xyz7 = origin + xyz5;
					Reference dimRef4 = dim.References.get_Item(i + 1);
					lstDimInfos.Add(new ConnectionDimension.DimensionReferenceInfo(document, dimRef4)
					{
						Point = xyz7,
						DistParam = this.GetParamByPointOnUnboundCurve(lineUnboundDimBase, xyz7)
					});
				}
			}
		}

		private string GetDimensionShareParam(Dimension sourceDim, List<Dimension> targetDims)
		{
			string result = null;
			string dimFlag = DimPublicFunc.GetDimFlag(sourceDim);
			if (!string.IsNullOrEmpty(dimFlag))
			{
				result = dimFlag;
				return result;
			}
			foreach (Dimension dim in targetDims)
			{
				dimFlag = DimPublicFunc.GetDimFlag(dim);
				if (!string.IsNullOrEmpty(dimFlag))
				{
					result = dimFlag;
					return result;
				}
			}
			return result;
		}

		private List<XYZ> GetGetDimensionMarkPoint(XYZ direction, DimensionSegment segment)
		{
			List<XYZ> list = new List<XYZ>();
			XYZ origin = segment.Origin;
			double num = segment.Value ?? -1.0;
			if (num > 0.0)
			{
				XYZ item = origin + -direction * (num / 2.0);
				list.Add(item);
				item = origin + direction * (num / 2.0);
				list.Add(item);
			}
			return list;
		}

		private List<SegmentReferenceRelation> GetSegmentReferenceRelation(Dimension dim)
		{
			List<SegmentReferenceRelation> list = new List<SegmentReferenceRelation>();
			Curve curve = dim.Curve;
			ReferenceArray references = dim.References;
			DimensionSegmentArray segments = dim.Segments;
            XYZ origin = segments.get_Item(0).Origin;
			XYZ direction = (segments.get_Item(segments.Size - 1).Origin - origin).Normalize();
			for (int i = 0; i < segments.Size; i++)
			{
				DimensionSegment dimensionSegment = segments.get_Item(i);
				List<Reference> list2 = new List<Reference>();
				list2.Add(references.get_Item(i));
                list2.Add(references.get_Item(i + 1));
				List<XYZ> getDimensionMarkPoint = this.GetGetDimensionMarkPoint(direction, dimensionSegment);
				if (getDimensionMarkPoint.Count > 0)
				{
					list.Add(new SegmentReferenceRelation
					{
						Index = i,
						DimSegment = dimensionSegment,
						DimReferences = list2,
						DimMarkPoints = getDimensionMarkPoint
					});
				}
			}
			return list;
		}

		private Document doc;

		private class DimensionReferenceInfo
		{
			public DimensionReferenceInfo(Autodesk.Revit.DB.Document doc, Reference dimRef)
			{
				this.m_doc = doc;
				this.DimReference = dimRef;
			}

			public XYZ Point { get; set; }

			public Reference DimReference { get; set; }

			public double DistParam { get; set; }

			private Document m_doc;
		}

		private class DimensionReferenceInfoComparer : IComparer<ConnectionDimension.DimensionReferenceInfo>
		{
			public int Compare(ConnectionDimension.DimensionReferenceInfo x, ConnectionDimension.DimensionReferenceInfo y)
			{
				if (Math.Abs(x.DistParam - y.DistParam) <= 1E-06)
				{
					return 0;
				}
				if (y.DistParam - x.DistParam > 1E-06)
				{
					return -1;
				}
				return 1;
			}
		}
	}
}
